/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
 */

package com.huawei.api.cmd;

import com.huawei.api.exception.InterruptException;

import lombok.Data;

import org.apache.commons.exec.CommandLine;
import org.apache.commons.exec.DefaultExecuteResultHandler;
import org.apache.commons.exec.DefaultExecutor;
import org.apache.commons.exec.ExecuteWatchdog;
import org.apache.commons.exec.Executor;
import org.apache.commons.exec.PumpStreamHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayOutputStream;
import java.io.IOException;

/**
 * 插件命令执行
 *
 * @since 2023/10/28
 */
@Data
public class CmdRunner {

    private static final Logger LOGGER = LoggerFactory.getLogger(CmdRunner.class);

    private static final Long MS = 1_000L;

    private ByteArrayOutputStream outStream = new ByteArrayOutputStream();

    private ByteArrayOutputStream errStream = new ByteArrayOutputStream();

    private DefaultExecuteResultHandler resultHandler = new DefaultExecuteResultHandler();

    private int exitValue = 0;

    /**
     * 执行任务检查
     *
     * @param commandLine 命令行
     * @param timeout     超时时间
     * @param isBlock     是否阻塞
     * @throws IOException 异常
     */
    public void run(CommandLine commandLine, int timeout, boolean isBlock) throws IOException {
        Executor executor = new DefaultExecutor();
        executor.setExitValue(exitValue);
        if (timeout > 0) {
            ExecuteWatchdog watchdog = new ExecuteWatchdog(MS * timeout);
            executor.setWatchdog(watchdog);
        }
        PumpStreamHandler streamHandler = new PumpStreamHandler(outStream, errStream);
        executor.setStreamHandler(streamHandler);
        executor.execute(commandLine, resultHandler);
        if (isBlock) {
            while (!resultHandler.hasResult()) {
                try {
                    resultHandler.waitFor();
                } catch (InterruptedException exception) {
                    LOGGER.error("wait for cmd result failed!", exception);
                }
            }
        }
    }

    /**
     * 关闭结果输出流
     *
     * @throws InterruptException 异常
     */
    private void closeOutStream() throws InterruptException {
        if (outStream != null) {
            try {
                outStream.close();
            } catch (IOException e) {
                throw new InterruptException("close out stream error!", e);
            }
            outStream = null;
        }
    }

    /**
     * 关闭异常输出流
     *
     * @throws InterruptException 异常
     */
    private void closeErrorStream() throws InterruptException {
        if (errStream != null) {
            try {
                errStream.close();
            } catch (IOException e) {
                throw new InterruptException("some error occur in close stream", e);
            }
            errStream = null;
        }
    }

    /**
     * 关闭流
     *
     * @throws InterruptException 异常
     */
    public void closeStream() throws InterruptException {
        closeOutStream();
        closeErrorStream();
    }

    /**
     * 关闭结果handler
     */
    private void closeResultHandler() {
        if (resultHandler != null) {
            resultHandler = null;
        }
    }

    /**
     * 重置
     *
     * @throws InterruptException 异常
     */
    public void reset() throws InterruptException {
        outStream = new ByteArrayOutputStream();
        errStream = new ByteArrayOutputStream();
        resultHandler = new DefaultExecuteResultHandler();
    }

    /**
     * 释放结果
     *
     * @throws InterruptException 异常
     */
    public void release() throws InterruptException {
        closeStream();
        closeResultHandler();
    }

}
